--- /dev/null
+/*
+ Copyright (C) 2016 Robert Lipe, robertlipe+source@gpsbabel.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+#include "defs.h"
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include "src/core/file.h"
+
+static gbfile* ofd;
+static const char MYNAME[] = "geojson";
+static char* compact_opt = NULL;
+static QJsonObject* track_object = NULL;
+static QJsonArray* track_coords = NULL;
+
+static arglist_t geojson_args[] = {
+ {"compact", &compact_opt, "Compact Output. Default is off.",
+ NULL, ARGTYPE_BOOL, ARG_NOMINMAX } ,
+ ARG_TERMINATOR
+};
+
+static void
+geojson_rd_init(const QString& fname) {
+}
+
+QJsonArray* feature_collection = nullptr;
+
+static void
+geojson_wr_init(const QString& fname) {
+ feature_collection = new QJsonArray;
+ ofd = gbfopen(fname, "w", MYNAME);
+}
+
+static void
+geojson_waypt_pr(const Waypoint* waypoint) {
+ QJsonObject object;
+ object["type"] = "Feature";
+
+ QJsonObject geometry;
+ geometry["type"] = "Point";
+
+ QJsonArray coords;
+ coords.append(waypoint->longitude);
+ coords.append(waypoint->latitude);
+ if (waypoint->altitude != unknown_alt && waypoint->altitude != 0) {
+ coords.append(waypoint->altitude);
+ }
+
+ geometry["type"] = "Point";
+ geometry["coordinates"] = coords;
+ object["geometry"] = geometry;
+
+ // Build up the properties.
+ QJsonObject properties;
+ if (!waypoint->shortname.isEmpty()) {
+ properties["name"] = waypoint->shortname;
+ }
+ if (!waypoint->description.isEmpty()) {
+ properties["description"] = waypoint->description;
+ }
+ UrlLink link = waypoint->GetUrlLink();
+ if (!link.url_.isEmpty()) {
+ properties["url"] = link.url_;
+ }
+ if (!link.url_link_text_.isEmpty()) {
+ properties["urlname"] = link.url_link_text_;
+ }
+ if (!properties.empty()) {
+ object["properties"] = properties;
+ }
+
+ feature_collection->append(object);
+}
+
+static void
+geojson_rd_deinit() {
+}
+
+static void
+geojson_wr_deinit(void) {
+ QJsonObject object;
+ object["type"] = "FeatureCollection";
+ object["features"] = *feature_collection;
+
+ QJsonDocument save(object);
+ QJsonDocument::JsonFormat style;
+ style = compact_opt ? QJsonDocument::Compact : QJsonDocument::Indented;
+ gbfputs(save.toJson(style),ofd);
+
+ gbfclose(ofd);
+ ofd = NULL;
+ delete feature_collection;
+ feature_collection = nullptr;
+}
+
+static void
+geojson_read(void) {
+}
+
+static void geojson_track_hdr(const route_head* track) {
+ track_object = new QJsonObject();
+
+ (*track_object)["type"] = "Feature";
+ track_coords = new QJsonArray();
+
+ QJsonObject properties;
+ if (!track->rte_name.isEmpty()) {
+ properties["name"] = track->rte_name;
+ }
+ (*track_object)["properties"] = properties;
+}
+
+static void geojson_track_disp(const Waypoint* trackpoint) {
+
+ QJsonArray coords;
+ coords.append(trackpoint->longitude);
+ coords.append(trackpoint->latitude);
+ if (trackpoint->altitude != unknown_alt && trackpoint->altitude != 0) {
+ coords.append(trackpoint->altitude);
+ }
+ (*track_coords).append(coords);
+}
+
+static void geojson_track_tlr(const route_head* track) {
+ QJsonObject geometry;
+ geometry["type"] = "LineString";
+ geometry["coordinates"] = *track_coords;
+ (*track_object)["geometry"] = geometry;
+ feature_collection->append(*track_object);
+ delete track_object;
+ track_object = NULL;
+ delete track_coords;
+ track_coords = NULL;
+}
+
+static void
+geojson_write(void) {
+ waypt_disp_all(geojson_waypt_pr);
+ track_disp_all(geojson_track_hdr, geojson_track_tlr, geojson_track_disp);
+}
+
+ff_vecs_t geojson_vecs = {
+ ff_type_file,
+ { (ff_cap)(/*ff_cap_read | */ff_cap_write), ff_cap_write, ff_cap_none },
+ geojson_rd_init,
+ geojson_wr_init,
+ geojson_rd_deinit,
+ geojson_wr_deinit,
+ geojson_read,
+ geojson_write,
+ NULL,
+ geojson_args,
+ CET_CHARSET_UTF8, 0 /* CET-REVIEW */
+};
--- /dev/null
+{
+ "features": [
+ {
+ "geometry": {
+ "coordinates": [
+ -87.134699999999995,
+ 35.972033332999999
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "Mountain Bike Heaven by susy1313",
+ "name": "GCEBB",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=3771",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.679550000000006,
+ 36.090683333000001
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "The Troll by a182pilot & Family",
+ "name": "GC1A37",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=6711",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.620116667000005,
+ 35.996266667
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "Dive Bomber by JoGPS & family",
+ "name": "GC1C2B",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=7211",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.648616666999999,
+ 36.038483333000002
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "FOSTER by JoGPS & Family",
+ "name": "GC25A9",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=9641",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.741766666999993,
+ 36.112183332999997
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "Logan Lighthouse by JoGps & Family",
+ "name": "GC2723",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=10019",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.790516667000006,
+ 36.064083332999999
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "Ganier Cache by Susy1313",
+ "name": "GC2B71",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=11121",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.809733332999997,
+ 36.087766666999997
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "Shy's Hill by FireFighterEng33",
+ "name": "GC309F",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=12447",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.891999999999996,
+ 36.057499999999997
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "GittyUp by JoGPS / Warner Parks",
+ "name": "GC317A",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=12666",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ },
+ {
+ "geometry": {
+ "coordinates": [
+ -86.867283333000003,
+ 36.082799999999999
+ ],
+ "type": "Point"
+ },
+ "properties": {
+ "description": "Inlighting by JoGPS / Warner Parks",
+ "name": "GC317D",
+ "url": "http://www.geocaching.com/seek/cache_details.asp?ID=12669",
+ "urlname": "Cache Details"
+ },
+ "type": "Feature"
+ }
+ ],
+ "type": "FeatureCollection"
+}
--- /dev/null
+<para>
+This module supports a subset of the <ulink url="http://geojson.org/">GeoJSON</ulink> format.
+</para>
+<para>
+GeoJSON is a poor fit for GPSBabel's internal data structures as GPSBabel
+was designed more around common GPS features (waypoints, tracks, routes)
+than about GIS-style concepts like MultiPolygons or Geometry Collections.
+In reality, for all but the most simple uses (such as converting a format
+that GPSBabel supports well to something like Leaflet, you should not expect
+high fidelity transfers through this format.
+</para>
+<para>
+Initially, only write support for waypoints and tracks is available.
+Waypoints are converted to a FeatureCollection of Points.
+The properties for name and description are written, where available.
+Tracks are converted to a LineString.
+</para>
+<para>
+Potential future work includes the ability to read Point Features and/or
+LineStrings as these would map into our concept of waypoints and
+routes/tracks.
+The potentially nested/recursive nature of GeoJSON in general would be
+an awkward implementation.
+</para>
+<para>
+Initial development was free-handed by looking at the <ulink url="https://tools.ietf.org/html/rfc7946 ">GeoJSON RFC</ulink>. Corner cases were handled by
+using <ulink url="http://www.gdal.org/ogr2ogr.html">GDAL's ogr2ogr</ulink>
+to convert GPX to JSON and compare the output. The results were then
+ <ulink url="http://geojsonlint.com/">JSON validated</ulink> and viewed on
+ <ulink url="http://geojson.io/">JSON web viewer</ulink>.
+</para>